//
//  main.m
//  SlimMachO
//
//  Created by  JasonLee on 2022/1/10.
//

#import <Foundation/Foundation.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <sys/stat.h>
    


//
#define IS_64_BIT(x) ((x) == MH_MAGIC_64 || (x) == MH_CIGAM_64)
#define IS_LITTLE_ENDIAN(x) ((x) == FAT_CIGAM || (x) == MH_CIGAM_64 || (x) == MH_CIGAM)
#define SWAP32(x, magic) (IS_LITTLE_ENDIAN(magic)? OSSwapInt32(x): (x))
#define SWAP64(x, magic) (IS_LITTLE_ENDIAN(magic)? OSSwapInt64(x): (x))

#define ROUND_UP(x, y) (((x) + (y) - 1) & -(y))

#define ABSDIFF(x, y) ((x) > (y)? (uintmax_t)(x) - (uintmax_t)(y): (uintmax_t)(y) - (uintmax_t)(x))

#define BUFSIZE 512

#define LC_COMMAND_NAME "LC_LOAD_DYLIB"
enum TJJ_CPU_SUBTYPE {
    TJJ_CPU_SUBTYPE_ARM64_ALL = 0,
    TJJ_CPU_SUBTYPE_ARM64_V8 = 1,
    TJJ_CPU_SUBTYPE_ARM64E = 2,
};
int slim_binary(const char *file, enum TJJ_CPU_SUBTYPE cpu_sub_type)
{
    int ret = 0;
    FILE *f = fopen(file, "r");
    if (!f) {
        printf("fail to open %s, %d", file, errno);
        return -1;
    }

    uint32_t magic;
    fread(&magic, sizeof(uint32_t), 1, f);
    if ((magic != FAT_MAGIC) &&
        (magic != FAT_CIGAM)) {
        return ret;
    }

    rewind(f);

    struct fat_header fh;
    fread(&fh, sizeof(fh), 1, f);
    uint32_t nfat_arch = SWAP32(fh.nfat_arch, magic);

    struct fat_arch archs[nfat_arch];
    fread(archs, sizeof(archs), 1, f);
    
    uint32_t x86_64_offset = 0;
    uint32_t x86_64_size = 0;
    BOOL hasArm64e = NO;
    cpu_subtype_t cpusubtype;
    switch (cpu_sub_type) {
        case TJJ_CPU_SUBTYPE_ARM64_ALL:
            cpusubtype = CPU_SUBTYPE_ARM64_ALL;
            break;
        case TJJ_CPU_SUBTYPE_ARM64_V8:
            cpusubtype = CPU_SUBTYPE_ARM64_V8;
            break;
        case TJJ_CPU_SUBTYPE_ARM64E:
            cpusubtype = CPU_SUBTYPE_ARM64E;
            break;
        default:
            break;
    }
    for(int i = 0; i < nfat_arch; i++) {
        cpu_type_t cpu_type = SWAP32(archs[i].cputype, magic);
        cpu_subtype_t cpu_subtype = SWAP32(archs[i].cpusubtype, magic);
        if ((cpu_type == CPU_TYPE_X86_64) &&
            (cpu_subtype & CPU_SUBTYPE_X86_ALL) == CPU_SUBTYPE_X86_ALL) {
            x86_64_offset = SWAP32(archs[i].offset, magic);
            x86_64_size = SWAP32(archs[i].size, magic);
        } else if ((cpu_type == CPU_TYPE_ARM64) && (cpu_subtype & cpusubtype) == cpusubtype) {
            hasArm64e = YES;
        }
    }
    
    if (!x86_64_offset || !x86_64_size || !hasArm64e) {
        return ret;
    }
    
    fseeko(f, x86_64_offset, SEEK_SET);
    char *buf = (char *)malloc(x86_64_size);
    if (!buf) {
        return -1;
    }
    fread(buf, x86_64_size, 1, f);
    
    char tmp[1024] = {0};
    sprintf(tmp, "%s.tmp", file);
    FILE *tmpf = fopen(tmp, "w+");
    fwrite(buf, x86_64_size, 1, tmpf);
    fflush(tmpf);
    ftruncate(fileno(tmpf), x86_64_size);
    
    free(buf);
    fclose(f);
    fclose(tmpf);
    
    unlink(file);
    chmod(tmp, 0777);

    rename(tmp, file);
    return ret;
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        slim_binary("/Users/jasonlee/Desktop/Google Chrome2",TJJ_CPU_SUBTYPE_ARM64_ALL);
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}









